package com.marion.ddd.service;

import com.marion.ddd.entity.AccountEntity;
import com.marion.ddd.entity.AccountRecordEntity;
import com.marion.ddd.enums.OverdraftPolicy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.Optional;

@Service
@Slf4j
public class TransferAccountService {

    @Autowired
    private TAccountService tAccountService;

    @Autowired
    private TAccountRecordService tAccountRecordService;

    /**
     * 转账逻辑 - 数据驱动设计
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean transferAccount(int fromUid, int toUid, long amount) {
        /**
         * 1. 查询账户余额是否足够
         * 2. 扣除用户余额，增加对方余额
         * 3. 写入转账记录
         * 4. 发送转账通知
         */
        AccountEntity from = tAccountService.queryById(fromUid);

        Optional<OverdraftPolicy> policy = OverdraftPolicy.fromValue(from.getOverdraft());
        if (policy.isEmpty()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "未知用户转账");
        }

        switch (policy.get()) {
            case NEVER:
                if (from.getAmount() < amount) {
                    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "余额不足");
                }
                break;
            case ALLOW:
                if (from.getAmount() < -10000) {
                    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "欠费超过1万");
                }
                break;
            default:
                break;
        }

        AccountEntity to = tAccountService.queryById(toUid);

        //2. 扣除用户余额，增加对方余额
        from.setAmount(from.getAmount() - amount);
        to.setAmount(to.getAmount() + amount);
        tAccountService.update(from);
        tAccountService.update(to);

        //3. 写入转账记录
        transfer(fromUid, toUid, amount, from, to);

        //4. 发送转账通知
        transferNotify();

        return true;
    }

    private void transfer(long fromUid, long toUid, long amount, AccountEntity from, AccountEntity to) {
        AccountRecordEntity fromRecord = AccountRecordEntity.builder()
                .fromUid(fromUid)
                .toUid(toUid)
                .type(0)
                .amount(amount)
                .fromRemain(from.getAmount())
                .toRemain(to.getAmount())
                .build();
        tAccountRecordService.insert(fromRecord);
        AccountRecordEntity toRecord = AccountRecordEntity.builder()
                .fromUid(toUid)
                .toUid(fromUid)
                .type(1)
                .amount(amount)
                .fromRemain(to.getAmount())
                .toRemain(from.getAmount())
                .build();
        tAccountRecordService.insert(toRecord);
    }

    private void transferNotify() {
        log.info("transfer notify");
    }

    /**
     * TODO 转账逻辑 - 策略设计模式实现
     */

    /**
     * 转账逻辑 - 领域驱动设计 - 用户分析法
     */
    public boolean domainTransferAccountUserCase() {
        /**
         * 1. 需求分析
         *      1. 用户A给用户B转账
         * 2. 领域分析
         *      1. 账户域
         *          1. 入账方法
         *          2. 出账方法
         *      2. 转账域
         *          1. 可多种火币的转账
         * 3. 领域建模
         * 4. 核心逻辑设计
         * 5. 技术细节
         * 二、用例分析法
         * 1. 场景。
         *  1> 用户A给用户B转账100元，需要银行卡信息
         *  2> 用户A给用户B转账100美元
         *  3> 用户账户不能转成负数
         *  4> 用户A给用户B转账100金币，不需要银行卡信息
         * 2. 名词。
         *  1> 用户A/用户B -> 用户
         *  2> 用户A账户/用户A账户 -> 用户账户
         *  3> 100元/美元/金币 -> 货币
         * 3. 关联。
         *  1> 转账。A -> B
         *  2> 用户A拥有账户。
         *  3> 账户拥有多种货币。
         * 4. 名词添加属性。
         *  1> 用户：姓名、昵称、性别、年龄
         *  2> 账户：姓名、银行卡号、开户行、余额
         *  3> 货币：类型
         * 5. 模型精化
         */

        return true;
    }

    /**
     * 转账逻辑 - 领域驱动设计 - 四色建模
     */
    public boolean domainTransferAccount4ColorModel() {
        /**
         *
         */

        return true;
    }

}
